package com.gitee.easyopen;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.net.URLDecoder;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSON;
import com.auth0.jwt.interfaces.Claim;
import com.gitee.easyopen.auth.Oauth2Manager;
import com.gitee.easyopen.auth.OpenUser;
import com.gitee.easyopen.bean.ApiDefinition;
import com.gitee.easyopen.bean.DefinitionHolder;
import com.gitee.easyopen.exception.ApiException;
import com.gitee.easyopen.exception.MethodInvokeException;
import com.gitee.easyopen.interceptor.ApiInterceptor;
import com.gitee.easyopen.jwt.JwtService;
import com.gitee.easyopen.message.Errors;
import com.gitee.easyopen.util.RequestUtil;

/**
 * 处理客户端请求分发
 * 
 * @author tanghc
 *
 */
public class ApiInvoker implements Invoker {

    private static final Logger logger = LoggerFactory.getLogger(ApiInvoker.class);
    
    private static final String UTF8 = "UTF-8";
    private static final String FORMAT_JSON = "json";
    private static final String FORMAT_XML = "xml";
    private static final String AUTHORIZATION = "Authorization";
    private static final String PREFIX_BEARER = "Bearer ";
    private static final ApiInterceptor[] EMPTY_INTERCEPTOR_ARRAY = {};
    
    
    private ApiConfig apiConfig; // 配置项
    private Validator validator; // 负责校验
    private ResultCreator resultCreator; // 负责生成最终结果

    private ResultSerializer jsonFormatter; // 负责把最终结果格式化成json
    private ResultSerializer xmlFormatter; // 负责把最终结果格式化成xml
    
    private RespWriter respWriter;
    private JwtService jwtService;
    
    private ParamParser paramParser;
    private Encrypter encrypter;
    
    public ApiInvoker() {
        super();
    }

    public ApiInvoker(ApiConfig apiConfig) {
        super();
        this.apiConfig = apiConfig;
        this.init(apiConfig);
    }
    
    private void init(ApiConfig apiConfig) {
        this.validator = apiConfig.getValidator();
        this.resultCreator = apiConfig.getResultCreator();

        this.jsonFormatter = apiConfig.getJsonResultSerializer();
        this.xmlFormatter = apiConfig.getXmlResultSerializer();
        
        this.respWriter = apiConfig.getRespWriter();
        
        this.jwtService = apiConfig.getJwtService();
        this.paramParser = apiConfig.getParamParser();
        this.encrypter = apiConfig.getEncrypter();
    }

    @Override
    public void invoke(HttpServletRequest request, HttpServletResponse response) {
        ApiContext.setRequest(request);
        Object result = null;
        ApiParam param = null;
        try {
            param = this.paramParser.parse(request);
            ApiContext.setApiParam(param);
            this.initJwtInfo(request, param);
            logger.info("收到客户端请求,ip={},参数={}", RequestUtil.getClientIP(request), param.toJSONString());
            result = this.doInvoke(param,request,response);
        } catch (ApiException e) {
            result = this.getResultCreator().createErrorResult(e.getCode(), e.getMessage(), e.getData());
        } catch (Throwable e) {
            result = this.caugthException(e);
        }

        if(result != null) {
            String format = param == null ? ApiParam.DEFAULT_FORMAT : param.fatchFormat();
            this.responseResult(response, result, format);
        }
    }
    
    @Override
    public void caugthException(HttpServletResponse response, Throwable e) {
        Result result = this.caugthException(e);
        this.responseResult(response, result, FORMAT_JSON);
    }

    // 捕获异常
    protected Result caugthException(Throwable e) {
        Throwable ex = e;
        if(e instanceof InvocationTargetException) {
            ex = ((InvocationTargetException)e).getTargetException();
        }
        logger.error(ex.getMessage(), ex);
        return this.getResultCreator().createErrorResult(Errors.SYS_ERROR.getCode(), ex.getMessage(), null);
    }
    
    private void initJwtInfo(HttpServletRequest request,ApiParam param) {
        String jwt = this.getHeader(request, AUTHORIZATION);
        if(jwt == null) {
            return;
        }
        if(jwt.startsWith(PREFIX_BEARER)) {
            jwt = jwt.replace(PREFIX_BEARER, "");
            Map<String, Claim> data = this.jwtService.verfiyJWT(jwt);
            ApiContext.setJwtData(data);
        } 
    }
    
    protected String getHeader(HttpServletRequest request, String key) {
        String value = request.getHeader(key);
        if(value == null) {
            return null;
        }
        if(ApiContext.isEncryptMode()) {
            value = ApiContext.decryptAES(value);
        }
        return value;
    }
    
    
    /**
     * 写数据到客户端
     * @param response
     * @param result 结果
     * @param format 返回类型，json,xml之一
     */
    public void responseResult(HttpServletResponse response, Object result, String format) {
        if (FORMAT_XML.equalsIgnoreCase(format)) {
            String text = this.xmlFormatter.serialize(result);
            this.respWriter.writeText(response, text);
        } else {
            String json = this.jsonFormatter.serialize(result);
            if(ApiContext.isEncryptMode()) { // 对结果加密
                String randomKey = ApiContext.getRandomKey();
                String text;
                try {
                    text = this.encrypter.aesEncryptToHex(json, randomKey);
                    this.respWriter.writeText(response, text);
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                    throw Errors.ERROR_SSL.getException();
                }
            }else {
                this.respWriter.writeJson(response, json);
            }
        }
    }

    /**
     * 调用接口，返回业务结果
     * @param param
     * @return
     * @throws Throwable
     */
    protected Object doInvoke(ApiParam param,HttpServletRequest request, HttpServletResponse response) throws Throwable {
        ApiDefinition definition = DefinitionHolder.getByParam(param);
        if (definition == null) {
            throw Errors.NO_API.getException(param.fatchName(), param.fatchVersion());
        }
        param.setIgnoreSign(definition.isIgnoreSign());
        param.setIgnoreValidate(definition.isIgnoreValidate());
        
        this.getValidator().validate(param);

        String busiJsonData = param.fatchData(); // 业务参数json格式
        busiJsonData = URLDecoder.decode(busiJsonData, UTF8);
        // 业务参数Class
        Class<?> arguClass = definition.getMethodArguClass();

        Object methodArgu = null;

        if (arguClass != null) {
            methodArgu = JSON.parseObject(busiJsonData, arguClass);
            this.getValidator().validateBusiParam(methodArgu);
        }
        
        Object invokeResult = null;
        int interceptorIndex = 0;
        ApiInterceptor[] interceptors = this.apiConfig.getInterceptors();
        if(interceptors == null) {
            interceptors = EMPTY_INTERCEPTOR_ARRAY;
        }
        //1. 调用preHandle
        for (int i = 0; i < interceptors.length; i++) {
            ApiInterceptor interceptor = interceptors[i];  
            if (interceptor.match(definition) && !interceptor.preHandle(request, response, definition.getHandler(),methodArgu)) {  
                //1.1、失败时触发afterCompletion的调用  
                triggerAfterCompletion(definition, interceptorIndex, request, response, methodArgu, null,null);  
                return null;  
            }  
            interceptorIndex = i;//1.2、记录当前预处理成功的索引  
        }  
        
        try {
            try {
                // 调用method方法
                if (methodArgu == null) {
                    invokeResult = definition.getMethod().invoke(definition.getHandler());
                } else {
                    invokeResult = definition.getMethod().invoke(definition.getHandler(), methodArgu);
                }
            } catch (Exception e) {
                throw new MethodInvokeException(e);
            }
            //3、调用postHandle,业务方法调用后处理（逆序）  
            for (int i = interceptors.length - 1; i >= 0; i--) {  
                ApiInterceptor interceptor = interceptors[i];  
                if(interceptor.match(definition)) {
                    interceptor.postHandle(request, response, definition.getHandler(), methodArgu, invokeResult);  
                }
            }  
            
            if(invokeResult == null) {
                invokeResult = new EmptyObject();
            }
            
            Object retObj = invokeResult; // 最终返回的对象
            
            if(definition.isWrapResult()) { // 对返回结果包装
                retObj = resultCreator.createResult(invokeResult);
            }
            //4、触发整个请求处理完毕回调方法afterCompletion  
            triggerAfterCompletion(definition, interceptorIndex, request, response, methodArgu, retObj, null);
            
            return retObj;
        } catch (MethodInvokeException e) {
            Exception ex = e.getTarget();
            this.triggerAfterCompletion(definition, interceptorIndex, request, response, methodArgu, invokeResult, ex);
            throw ex;
        }
    }
    
    // triggerAfterCompletion方法  
    private void triggerAfterCompletion (ApiDefinition definition,int interceptorIndex,  
                HttpServletRequest request, HttpServletResponse response, Object argu,Object result, Exception e) throws Exception {  
        // 5、触发整个请求处理完毕回调方法afterCompletion （逆序从1.2中的预处理成功的索引处的拦截器执行）  
        ApiInterceptor[] interceptors = this.apiConfig.getInterceptors();  
        
        if(interceptors != null && interceptors.length > 0) {
            for (int i = interceptorIndex; i >= 0; i--) {  
                ApiInterceptor interceptor = interceptors[i];
                if(interceptor.match(definition)) {
                    interceptor.afterCompletion(request, response, definition.getHandler(),argu,result, e);
                }
            }  
        }
    }  
    
    /**
     * 验证accessToken
     * @param param
     * @throws Oauth2UnauthorizedException
     */
    protected void checkAccessToken(ApiParam param) {
        Oauth2Manager manager = apiConfig.getOauth2Manager();
        String accessToken = param.fatchAccessToken();
        if(StringUtils.isEmpty(accessToken)) {
            return;
        }
        try {
            OpenUser openUser = manager.getUserByAccessToken(accessToken);
            ApiContext.setAccessTokenUser(openUser);
        } catch (ApiException e) {
            throw e;
        } catch (Exception e) {
            throw new ApiException(e);
        }
    }
    
    private static class EmptyObject implements Serializable {
        private static final long serialVersionUID = 1713263598232463135L;
    }

    @Override
    public ApiConfig getApiConfig() {
        return apiConfig;
    }

    @Override
    public void setApiConfig(ApiConfig apiConfig) {
        this.apiConfig = apiConfig;
        this.init(apiConfig);
    }

    public Validator getValidator() {
        return validator;
    }

    public void setValidator(Validator validator) {
        this.validator = validator;
    }

    public ResultCreator getResultCreator() {
        return resultCreator;
    }

    public void setResultCreator(ResultCreator resultCreator) {
        this.resultCreator = resultCreator;
    }

}
